package cn.tworice.netty.hanlder;

import cn.tworice.netty.message.MessageManager;
import cn.tworice.netty.session.Session;
import cn.tworice.netty.session.SessionManager;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * SimpleChannelInboundHandler 会自动释放处理完的消息对象，以避免内存泄漏
 **/
@Slf4j
@Component
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    @Resource
    private SessionManager sessionManager;

    @Resource
    private MessageManager messageManager;

    /**
     * 收到WebSocket消息帧
     * @param ctx 客户端
     * @param frame 消息帧
     **/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        if (frame instanceof PingWebSocketFrame) {
            pingWebSocketFrameHandler(ctx, (PingWebSocketFrame) frame);
        } else if (frame instanceof TextWebSocketFrame) {
            textWebSocketFrameHandler(ctx, (TextWebSocketFrame) frame);
        } else if (frame instanceof CloseWebSocketFrame) {
            closeWebSocketFrameHandler(ctx, (CloseWebSocketFrame) frame);
        }
    }

    /**
     * 监听用户连接
     * @param ctx 客户端
     * @param msg 请求体
     **/
//    @Override
//    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        if (msg instanceof FullHttpRequest) {
//            fullHttpRequestHandler(ctx, (FullHttpRequest) msg);
//        }
////        super.channelRead(ctx, msg);
//    }

    /**
     * 处理连接请求，客户端WebSocket发送握手包时会执行这一次请求
     * @param ctx 客户端
     * @param request
     */
    private void fullHttpRequestHandler(ChannelHandlerContext ctx, FullHttpRequest request) {
        String uri = request.uri();
//        Map<String, String> params = RequestUriUtils.getParams(uri);
//        log.debug("客户端请求参数：{}", params);
//        // 判断请求路径是否跟配置中的一致
//        if (webSocketProperties.getPath().equals(RequestUriUtils.getBasePath(uri)))
//            // 因为有可能携带了参数，导致客户端一直无法返回握手包，因此在校验通过后，重置请求路径
//            request.setUri(webSocketProperties.getPath());
//        else
//            ctx.close();
    }

    /**
     * 连接建立时调用
     * @param ctx 客户端
     **/
    @Override
    public void handlerAdded(ChannelHandlerContext ctx){
        log.debug("客户端尝试建立连接：{}",ctx.channel().id().asLongText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx){
        // 连接关闭时调用
        Session session = sessionManager.removeChannel(ctx.channel());
        messageManager.offLine(session);
    }

    /**
     * 发生异常时调用
     * @param ctx 客户端
     * @param cause 异常
     **/
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("Netty控制器异常{}", ctx.channel().id().asLongText(), cause);
    }

    /**
     * 客户端发送断开请求处理
     */
    private void closeWebSocketFrameHandler(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
        ctx.close();
        Session session = sessionManager.removeChannel(ctx.channel());
        messageManager.offLine(session);
    }

    /**
     * 处理收到消息
     * 只用来建立唯一标识，并向下传递
     */
    private void textWebSocketFrameHandler(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
        log.debug("收到消息:{}",frame.text());
        this.messageManager.receiveMessage(ctx, frame);
    }

    /**
     * 处理客户端心跳包
     */
    private void pingWebSocketFrameHandler(ChannelHandlerContext ctx, PingWebSocketFrame frame) {
        ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
    }
}

